AWS CDK で AWS Lambda のイベントフィルタリング条件の暗号化が可能になっていました

AWS CDK で AWS Lambda のイベントフィルタリング条件の暗号化が可能になっていました

Clock Icon2024.08.18

こんにちは、製造ビジネステクノロジー部の若槻です。

AWS CDK の最新のリリースで下記のアップデートが追加されていました。
https://github.com/aws/aws-cdk/releases/tag/v2.152.0

lambda: support filter criteria encryption (6aa72a2)

アップデート概要だけ見ても私はどんなアップデート内容なのかピンと来なかったので、そもそもどんな機能であるのか含めて、実際に AWS CDK で設定して確認してみました。

確認してみた

アップデートの README を確認してみる

アップデートにより追記されていた README は下記の通りです。

packages/aws-cdk-lib/aws-lambda/README.md
By default, Lambda will encrypt Filter Criteria using AWS managed keys. But if you want to use a self managed KMS key to encrypt the filters, You can specify the self managed key using the `filterEncryption` property.

```ts
import * as eventsources from 'aws-cdk-lib/aws-lambda-event-sources';
import * as dynamodb from 'aws-cdk-lib/aws-dynamodb';
import { Key } from 'aws-cdk-lib/aws-kms';

declare const fn: lambda.Function;
const table = new dynamodb.Table(this, 'Table', {
  partitionKey: {
    name: 'id',
    type: dynamodb.AttributeType.STRING,
  },
  stream: dynamodb.StreamViewType.NEW_IMAGE,
});
// Your self managed KMS key
const myKey = Key.fromKeyArn(
  this,
  'SourceBucketEncryptionKey',
  'arn:aws:kms:us-east-1:123456789012:key/<key-id>',
);

fn.addEventSource(new eventsources.DynamoEventSource(table, {
  startingPosition: lambda.StartingPosition.LATEST,
  filters: [lambda.FilterCriteria.filter({ eventName: lambda.FilterRule.isEqual('INSERT') })],
  filterEncryption: myKey,
}));

> Lambda requires allow `kms:Decrypt` on Lambda principal `lambda.amazonaws.com` to use the key for Filter Criteria Encryption. If you create the KMS key in the stack, CDK will automatically add this permission to the Key when you creates eventSourceMapping. However, if you import the key using function like `Key.fromKeyArn` then you need to add the following permission to the KMS key before using it to encrypt Filter Criteria

```json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "lambda.amazonaws.com"
      },
      "Action": "kms:Decrypt",
      "Resource": "*"
    }
  ]
}
```

AWS Lambda の EventSourceMapping では、次のサービスで EventFilter を使用してフィルター条件を指定し、関数に送信されるレコードをフィルターすることができます。

  • Amazon DynamoDB
  • Amazon Kinesis Data Streams
  • Amazon MQ
  • Amazon Managed Streaming for Apache Kafka (Amazon MSK)
  • Self-managed Apache Kafka
  • Amazon Simple Queue Service (Amazon SQS)

https://docs.aws.amazon.com/lambda/latest/dg/invocation-eventfiltering.html

そしてデフォルトでは、Lambda は AWS 管理キーを使用してフィルター条件(filter criteria)を暗号化するが、filterEncryption プロパティを指定してカスタマー管理キーを使ったフィルター条件の暗号化を AWS CDK での実装で可能とする、というのが今回のアップデートのようです。

フィルター条件にセンシティブな情報が含まれている場合、フィルター条件をカスタマー管理キーで暗号化することでセキュリティを向上させることができそうですね。

実装、動作確認

次のように DynamoDB stream に対して Lambda 関数をトリガーする実装を AWS CDK で作成してみます。filterEncryption には CDK で作成したカスタマー管理キーを指定しています。

lib/cdk-sample-stack.ts
import {
  Stack,
  RemovalPolicy,
  aws_kms,
  aws_lambda,
  aws_lambda_nodejs,
  aws_lambda_event_sources,
  aws_dynamodb,
} from 'aws-cdk-lib';
import { Construct } from 'constructs';

export class CdkSampleStack extends Stack {
  constructor(scope: Construct, id: string) {
    super(scope, id);

    const myTable = new aws_dynamodb.Table(this, 'MyTable', {
      partitionKey: { name: 'id', type: aws_dynamodb.AttributeType.STRING },
      stream: aws_dynamodb.StreamViewType.OLD_IMAGE,
      removalPolicy: RemovalPolicy.DESTROY,
    });

    const myKey = new aws_kms.Key(this, 'MyKey', {
      removalPolicy: RemovalPolicy.DESTROY,
    });

    const myFunction = new aws_lambda_nodejs.NodejsFunction(this, 'MyFunction');

    myFunction.addEventSource(
      new aws_lambda_event_sources.DynamoEventSource(myTable, {
        startingPosition: aws_lambda.StartingPosition.LATEST,
        batchSize: 1,
        filters: [
          aws_lambda.FilterCriteria.filter({
            eventName: aws_lambda.FilterRule.isEqual('INSERT'),
          }),
        ],
        filterEncryption: myKey, // カスタマー管理キーを指定
      })
    );
  }
}

上記実装のデプロイにより作成された Lambda 関数のトリガー設定です。Encrypt filter criteria with customer managed KMS key: Yes とあり、カスタマー管理キーによるフィルター条件の暗号化が有効化されていることが分かります。ここで、フィルター条件がコンソール上で暗号化されるわけではないことに注意してください。

テーブルにデータを INSERT すると、Lambda 関数がトリガーされることが確認できます。

カスタマー管理キーが必要となっていることを確認する

ここで、カスタマー管理キー側では次のような権限が設定されています。前述の README にあった通り kms:Decrypt の権限は CDK により自動的に追加されています。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::300561038900:root"
            },
            "Action": "kms:*",
            "Resource": "*"
        },
        {
            "Effect": "Allow",
            "Principal": {
                "Service": "lambda.amazonaws.com"
            },
            "Action": "kms:Decrypt",
            "Resource": "*"
        }
    ]
}

上記の kms:Decrypt の権限をキーから削除してみます。その上でテーブルにデータを INSERT すると、Lambda 関数がトリガーされません。コンソールからトリガー設定を確認するとLast processing result: PROBLEM: Lambda does not have permissions to call Decrypt on KMSとなっており、権限不足によりカスタマー管理キーの使用が失敗していることが分かります。

このように Lambda 関数がイベントソースマッピングのフィルター条件を参照するためにカスタマー管理キーが必要となっていることが確認できました。

filterEncryption は裏仕様っぽさがある

今回 CDK で実装したスタックの CloudFormation テンプレートを見てみると、次のように KmsKeyArn というプロパティが追加されていることが分かります。このプロパティが filterEncryption に対応しているものと思われます。

"MyFunctionDynamoDBEventSourceCdkSampleStackMyTable393F30B56FD7A5C7": {
      "Type": "AWS::Lambda::EventSourceMapping",
      "Properties": {
        "BatchSize": 1,
        "EventSourceArn": {
          "Fn::GetAtt": [
            "MyTable794EDED1",
            "StreamArn"
          ]
        },
        "FilterCriteria": {
          "Filters": [
            {
              "Pattern": "{\"eventName\":[\"INSERT\"]}"
            }
          ]
        },
        "FunctionName": {
          "Ref": "MyFunction3BAA72D1"
        },
        "KmsKeyArn": {
          "Fn::GetAtt": [
            "MyKey6AB29FA6",
            "Arn"
          ]
        },
        "MaximumRetryAttempts": 0,
        "StartingPosition": "LATEST"
      },
      "Metadata": {
        "aws:cdk:path": "CdkSampleStack/MyFunction/DynamoDBEventSource:CdkSampleStackMyTable393F30B5/Resource"
      }
    },

このプロパティは AWS::Lambda::EventSourceMapping の CloudFormation ドキュメントにありはするのですが、どんなプロパティなのかの説明はありませんでした。
https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-eventsourcemapping.html#cfn-lambda-eventsourcemapping-kmskeyarn

また CreateEventSourceMapping の API ドキュメントに至っては対応するプロパティの記載すらありませんでした。
https://docs.aws.amazon.com/lambda/latest/api/API_CreateEventSourceMapping.html

このように filterEncryption はイベントソースマッピングの裏仕様のような機能を使うためのプロパティのような感じがしますね。

おわりに

AWS CDK で AWS Lambda のイベントフィルタリング条件の暗号化が可能になっていたので共有しました。

セキュリティ要件がよほど厳しい場合を除いて積極的には使わないかもしれませんが、セキュリティを向上させるための機能として知っておくと良いかもしれません。

以上

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.